#pragma once 

#include<iostream>
#include<vector>
#include<queue>
#include<unistd.h>
#include<pthread.h>
#include<string>
#include"Task.hpp"

const static int N = 5;

template <class T>
class ThreadPool
{
public:
    ThreadPool(int num=N):_num(num),_threads(num)
    {
        pthread_mutex_init(&_lock,nullptr);
        pthread_cond_init(&_cond,nullptr);
    }

    void init()
    {
        //TODO
    }
    void lockQueue()
    {
        pthread_mutex_lock(&_lock);
    }
    void unlockQueue()
    {
        pthread_mutex_unlock(&_lock);
    }
    void threadWait()
    {
        pthread_cond_wait(&_cond,&_lock);
    }
    void threadWakeup()
    {
        pthread_cond_signal(&_cond);
    }
    //判断任务队列是否为空
    bool isEmpty()
    {
        return _tasks.empty();
    }
    static void* threadRoutine(void* args)
    {
        pthread_detach(pthread_self());//分离线程,任务处理完毕，自行退出
        ThreadPool<T> * tp=static_cast<ThreadPool<T>*>(args);//传了this指针过来，也就是自己
        while(true)
        {
            //1.检测任务队列中有没有任务
            //2.有：则处理任务
            //3.无：则等待任务
            //细节：多线程并发访问任务队列，一定要加锁
            //在临界区内部，我们都有对临界资源：任务队列进行访问
            tp->lockQueue();
            while(tp->isEmpty())
            {
                //等待,cond 
                tp->threadWait();
            }
            //获取任务，处理任务
            T t = tp->popTask();//从公共任务队列，将任务拿到多线程私有区域
            tp->unlockQueue();

            //处理任务，不应该在临界区中处理，因为处理任务是多线程的事，不访问任务队列
            t();
            std::cout << "thread handler done, result: " << t.formatRes() << std::endl;
        }
    }
    
    void pushTask(const T& t)
    {
        lockQueue();
        _tasks.push(t);
        pthread_cond_signal(&_cond);
        unlockQueue();
        
    }
    //获取任务
    T popTask()
    {
        T t = _tasks.front();
        _tasks.pop();
        return t;
    }
    void start()
    {
        for(int i=0;i<_num;i++)
        {
            pthread_create(&_threads[i],nullptr,threadRoutine,this);
        }
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_cond);
    }
private:
    std::vector<pthread_t> _threads;//用数组管理多线程
    int _num;//多线程数

    std::queue<T> _tasks;//任务队列

    pthread_mutex_t _lock;//因为多线程需要并发的访问任务队列，去获取任务，所以要对任务队列加锁
    pthread_cond_t _cond;//当任务队列为空，多线程就进行等待
};
